相信大家在偶尔看到一些库的源码时,总是能看到这样一句语法a === true && !!obj;, 是的,这里面用了!!obj 这样一个运算符。我们不禁好奇,这样做的结果不就是等于什么都没做吗?最近在看老姚的《underscore源码解析》恰好也有这么一段,作者做了解释,我自己也求证了一下。

_.isObject()方法

源码如下

1
2
3
4
_.isObject = function(obj){
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};

可以看到,如果 typeof 的值为‘function’则直接返回true, 否则返回是否是object。 但是其实多做了一步,作者解释是为了避免null的值。

后面的!!obj是拿到obj的布尔值。目的是为了排除null。

!!obj 返回了什么

我们申明简单的对象,以及空对象,可以直接看到这段表达式返回了什么

1
2
3
4
5
6
7
8
// 正常对象
var normalObj = {}
console.log(!normalObj) // false
console.log(!!normalObj) // ture

// null
console.log(!null) // true
console.log(!!null) // false

对于正常的非空对象,返回true,对于null则返回false。But wait!!, 这有啥用?难道我们平时不是直接 if(type === 'object' && obj) {} 就可以了吗?

!!obj 为什么要这么写

其实作者有举一个例子说明:

1
2
3
if (20 && 30) {
console.log('The result is ture') // 会执行
}

此处 if语句的返回结果显然是true, 但是 20 && 30 的返回结果其实是30(因为会读取最后一个的值,故而 20 && 30 && 10会返回10。同理,20 || 30 || 10会返回 20), 之所以返回了true是因为if语句进行的类型转换。

对于不在if语句以内的表达式,如果我们也需要返回bool值呢?是的这时候!!obj就派上了用场,他返回的是bool值,而且恰好和我们需求中的是否为null对应了起来。

因此 !! 并没有什么特殊的功能,他就是做了两次运算,然后返回了运算的结果, true 或者 false